#pragma once

#include <string>
#include <uv.h>

#include "UdpSocketBase.hpp"

//struct uv_udp_t;
class UdpSocket:  public UdpSocketBase {
public:
  /* Struct for the data field of uv_req_t when sending a datagram. */
  struct UvSendData {
    UdpSocket *socket{nullptr};
    uv_udp_send_t req;
    uint8_t store[1];
  };

public:
	UdpSocket();
	virtual ~UdpSocket();
  
public:
	int Listen(const char* ip, uint16_t port, const CALL_BACK& onReceiveData);
	int Listen(const char* ip, uint16_t port, const CALL_BACK& onReceiveData, const ON_CLOSE& onClose, const ON_SEND_FAILED& onSendFailed);
	void Destroy();
public:
	void Send(const uint8_t *data, size_t len, const struct sockaddr *addr);
	void Send(const uint8_t *data, size_t len, const char* ip,  uint16_t port);
public:
	const struct sockaddr *GetLocalAddress() const;
	std::string GetLocalIP();
	uint16_t GetLocalPort() const;
	size_t GetRecvBytes() const;
	size_t GetSentBytes() const;
public:
	void OnUVHandleAlloc(uv_buf_t *buf);
	void OnUvRecv(ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned int flags);
	void OnUvClosed();
	void OnUvSendError(int);
private:
	int SetLocalAddress();
private:
	static void default_OnClose() {}
	static void default_OnSendFailed(int){}
	static void default_OnReceive(const uint8_t*, size_t, const struct sockaddr*) {}
private:
	static void onClose(uv_handle_t *handle);
private:
	static const size_t READ_BUFFER_SIZE;
private:
	CALL_BACK receiveDataCallBack;
	ON_CLOSE onCloseCallBack;
	ON_SEND_FAILED onSendFailed;
	
	// Allocated by this (may be passed by argument).
	uv_udp_t *uvHandle {nullptr};
	bool isClosing { false };
	
	struct sockaddr_storage localAddr {};
	std::string localIP;
	uint16_t localPort{0};
	
	size_t recvBytes{0};
	size_t sentBytes{0};
	
	uint8_t* readBuffer;
};